package com.yunxi.module.system.service.oauth2.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.yunxi.framework.common.enums.CommonStatusEnum;
import com.yunxi.framework.common.util.string.StrUtils;
import com.yunxi.module.system.model.oauth2.OAuth2ClientDO;
import com.yunxi.module.system.enums.constant.ErrorCodeConstants;
import com.yunxi.module.system.mapper.oauth2.OAuth2ClientMapper;
import com.yunxi.module.system.redis.RedisKeyConstants;
import com.yunxi.module.system.service.oauth2.OAuth2ClientService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collection;

import static com.yunxi.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * <p>
 * OAuth2.0 Client Service 接口实现类
 * </p>
 *
 * @author lidy
 * @since 2024-05-08
 */
@Service
@Slf4j
@Validated
public class OAuth2ClientServiceImpl implements OAuth2ClientService {

    @Resource
    private OAuth2ClientMapper oAuth2ClientMapper;

    /**
     * 从缓存中，校验客户端是否合法
     *
     * @param clientId 客户端 ID
     * @return 客户端
     */
    @Override
    public OAuth2ClientDO validOAuthClientFromCache(String clientId) {
        return validOAuthClientFromCache(clientId, null, null, null, null);
    }

    /**
     * 从缓存中，校验客户端是否合法
     * 非空时，进行校验
     *
     * @param clientId            客户端编号
     * @param clientSecret        客户端秘钥
     * @param authorizedGrantType 授权方式
     * @param scopes              授权范围
     * @param redirectUri         重定向地址
     * @return 客户端
     */
    @Override
    public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType, Collection<String> scopes, String redirectUri) {
        // 校验客户端存在且开启
        OAuth2ClientDO clientDO = getSelf().getOAuth2ClientFromCache(clientId);
        // 客户端不存在
        if (clientDO == null) {
            throw exception(ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS);
        }
        // 客户端禁用
        if (CommonStatusEnum.isDisable(clientDO.getStatus())) {
            throw exception(ErrorCodeConstants.OAUTH2_CLIENT_DISABLE);
        }

        // 校验客户端秘钥
        if (StrUtil.isNotEmpty(clientSecret) && ObjectUtil.notEqual(clientDO.getSecret(), clientSecret)) {
            throw exception(ErrorCodeConstants.OAUTH2_CLIENT_CLIENT_SECRET_ERROR);
        }

        // 校验授权方方式
        if (StrUtil.isNotEmpty(authorizedGrantType) && !CollUtil.contains(clientDO.getAuthorizedGrantTypes(), authorizedGrantType)) {
            throw exception(ErrorCodeConstants.OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS);
        }

        // 校验授权范围
        if (CollUtil.isNotEmpty(scopes) && !CollUtil.containsAll(clientDO.getScopes(), scopes)) {
            throw exception(ErrorCodeConstants.OAUTH2_CLIENT_SCOPE_OVER);
        }

        // 校验回调地址
        if (StrUtil.isNotEmpty(redirectUri) && !StrUtils.startWithAny(redirectUri, clientDO.getRedirectUris())) {
            throw exception(ErrorCodeConstants.OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, redirectUri);
        }

        return clientDO;
    }

    /**
     * 从缓存中获得 OAuth2 客户端
     *
     * @param clientId 客户端编号
     * @return OAuth2 客户端
     */
    @Override
    @Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId", unless = "#result == null")
    public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) {
        return oAuth2ClientMapper.selectByClientId(clientId);
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自身的代理对象
     */
    private OAuth2ClientServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }
}
